#ifndef __UTILS_HPP__
#define __UTILS_HPP__

#include <vector>
#include <string>
#include <queue>
#include <mutex>

/******************************************************************************/

static void base64_encode(const std::vector<unsigned char>& data, std::vector<char>& result)
{
	const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	int n = data.size();
	result.resize( (n+2)/3*4 );
	int p = 0;
	for (int i=0; i<n; i+=3) {
		int in[3] = {data[i], (i+1<n)?data[i+1]:-1, (i+2<n)?data[i+2]:-1};
		int out[4] = {-1, -1, -1, -1};
		
		out[0] = (in[0]&0xff) >> 2;
		out[1] = (in[0]&0x03) << 4;
		if (in[1] != -1) {
			out[1] |= in[1] >> 4;
			out[2] = (in[1]&0x0f) << 2;
			if (in[2] != -1) {
				out[2] |= in[2] >> 6;
				out[3] = in[2]&0x3f;
			}
		}
		
		for (int j=0; j<4; j++) {
			result[p++] = (out[j]==-1) ? '=' : base64[out[j]];
		}
	}
}

/******************************************************************************/

struct UriInfo {
	std::string protocol;
	struct{
		std::string username, password;
	} auth;
	std::string host;
	std::string path;
	int port;
};
// uri: [protocol://][username:password@]{host}[:port][/path]
static UriInfo parse_uri(const std::string& uri)
{
	UriInfo info;
	std::string remain = uri;
	std::string auth;
	
	auto n = uri.find("://");
	if (n != std::string::npos) {
		info.protocol = uri.substr(0, n);
		remain = uri.substr(n+3);
	}
	
	n = remain.find('/');
	if (n != std::string::npos) {
		info.path = remain.substr(n);
		remain = remain.substr(0, n);
	}
	
	n = remain.find('@');
	if (n != std::string::npos) {
		auth = remain.substr(0, n);
		remain = remain.substr(n+1);
	}
	
	n = remain.find(':');
	if (n != std::string::npos) {
		info.host = remain.substr(0, n);
		info.port = atoi(remain.substr(n+1).c_str());
	} else {
		info.host = remain;
		info.port = -1;
	}
	
	if (!auth.empty()) {
		n = auth.find(':');
		if (n != std::string::npos) {
			info.auth.username = auth.substr(0, n);
			info.auth.password = auth.substr(n+1);
		} else {
			info.auth.username = auth;
		}
	}
	
	return info;
}

/******************************************************************************/

template<class T>
class SafetyQueue
{
private:
	mutable std::mutex mutex_;
	std::queue<T> queue_;

public:
	SafetyQueue() {}
	
	SafetyQueue(const SafetyQueue&) = delete;
	SafetyQueue& operator=(const SafetyQueue&) = delete;
	
	void put(const T& data) {
		std::lock_guard<std::mutex> lock(mutex_);
		queue_.push(data);
	}
	
	bool get(T& data) {
		std::lock_guard<std::mutex> lock(mutex_);
		if (queue_.empty()) return false;
		data = std::move(queue_.front());
		queue_.pop();
		return true;
	}
	
	bool empty() const {
		std::lock_guard<std::mutex> lock(mutex_);
		return queue_.empty();
	}
	
	bool size() const {
		std::lock_guard<std::mutex> lock(mutex_);
		return queue_.size();
	}
};

#endif
